1.5.1 Canvas 变换
1. 平移 (Translate)
画布的原始状态是以左上角为原点,向右是 X 轴正方向,向下是 Y 轴正方向。
参数:
float dx:水平方向平移的距离,正数为向正方向 (向右) 平移的量,负数为向负方向 (向左) 平移的量。
float dy:垂直方向平移的距离,正数为向正方向 (向下) 平移的量,负数为向负方向 (向上) 平移的量。
示例:
上述示例中,调用 canvas.translate(100, 100) 后,绿色矩形区域并没有随着画布移动。这是由于屏幕显示与 Canvas 根本不是一个概念!
2. 屏幕显示与 Canvas 的关系
Canvas 是一个很虚幻的概念,相当于一个透明图层。每次在 Canvas 上画图时 (调用 drawXXX 系列函数),都会先产生一个透明图层,然后在这个图层上画图,画完之后覆盖在屏幕上显示。所以,上述结果是经以下几个步骤形成的:
(1) 在调用 canvas.drawRect(rect1, paint_green) 时,产生一个 Canvas 透明图层,由于当时还没有对坐标系进行平移,所以坐标原点是 (0,0);在 Canvas 上画好之后,覆盖到屏幕上显示出来。过程如下图所示。
(2) 在调用 canvas.drawRect(rect1, paint_red) 时,又会产生一个全新的 Canvas 透明图层,但此时画布坐标已经改变了,即分别向右和向下移动了 100 像素,所以此时的绘图方式如下图所示 (合成视图,从上往下看的合成方式)。
总结:
- 当每次调用 drawXXX 系列函数来绘图时,都会产生一个全新的 Canvas 透明图层。
- 如果在调用 drawXXX 系列函数前,调用平移、旋转等函数对 Canvas 进行了操作,那么这个操作是不可逆的。每次产生的画布的最新位置都是这些操作后的位置。
- 在 Canvas 图层与屏幕合成时,超出屏幕范围的图像是不会显示出来的。
3. 旋转 (Rotate)
画布的旋转默认是围绕坐标原点来进行的。这里容易产生错觉,看起来是图片旋转了,其实我们旋转的是画布,以后在此画布上绘制的图形显示出来的时候看起来都是旋转的。
参数:
- float degrees:旋转的度数,正数指顺时针旋转。旋转中心点是 (0,0)。
- float px, py:指定旋转的中心点坐标 (px,py)。
示例:
旋转原理:
4. 缩放 (Scale)
变更坐标轴密度。
参数:
float sx:水平方向伸缩的比例。小数表示缩小,整数表示放大。
float sy:垂直方向伸缩的比例。小数表示缩小,整数表示放大。
示例:
蓝框是原坐标轴密度图形,红框是 X 轴密度缩小到 0.5 倍之后显示的图形。
5. 扭曲 (Skew)
|
|
参数:
float sx:将画布在 X 轴方向上倾斜相应的角度,sx 为倾斜角度的正切值。
float sy:将画布在 Y 轴方向上倾斜相应的角度,sy 为倾斜角度的正切值。
注意:这里都是倾斜角度的正切值。比如,在 X 轴方向上倾斜 60°,tan60=1.732。
示例:
6. 裁剪画布 (clip 系列函数)
裁剪画布是指利用 clip 系列函数,通过与 Rect、Path、Region 取交、并、差等集合运算来获得最新的画布形状。除调用 save()、restore() 函数以外,这个操作是不可逆的,一旦 Canvas 被裁剪,就不能恢复。
在使用裁剪画布系列函数时,需要禁用硬件加速功能。
setLayerType(LAYER_TYPE_SOFTWARE, null);
|
|
示例:
先把背景色涂成红色,显示在屏幕上,然后裁剪画布,最后将最新的画布涂成绿色。可见,绿色部分只有一小块,而不再是整个屏幕。
1.5.2 画布的保存与恢复
1. save() 和 restore() 函数
前面介绍的所有对画布的操作都是不可逆的,如果要对画布的大小和状态 (旋转角度、扭曲等) 进行实时保存和恢复,需要借助 save() 和 restore() 这两个函数。
save():每次调用,都会先保存当前画布的状态,然后将其放入特定的栈中。
restore():每次调用,都会把栈中顶层的画布状态取出来,并按照这个状态恢复当前的画布,然后在这个画布上作画。
示例:
2. restoreToCount(int saveCount) 函数
我们可以多次调用 save() 函数,但每次调用 restore() 函数,只会将顶层的画布状态出栈。有时可能只需要用到特定的画布,这就需要多次 出栈。为了解决这个问题,Google 提供了另一个出栈函数 restoreToCount(int saveCount)。
在利用 save() 函数保存画布时,会有一个 int 类型的返回值。该返回值是当前所保存的画布所在栈的索引。
而 restoreToCount() 函数的用法就是一直出栈,直到指定索引的画布出栈为止,即将指定索引的画布作为当前画布。
示例:
1.5.3 示例一:圆形头像
|
|
前面说过,在使用 clip 系列函数时,要禁用硬件加速功能。 setLayerType(LAYER_TYPE_SOFTWARE,null);
然而在手机型号 Pixel XL 上测试,是否禁用硬件加速功能并不影响 clip 函数。
1.5.4 示例二:裁剪动画
1. 原理
动画原理就是每次将裁剪区域变大,在裁剪区域内的图像就会显示出来,而裁剪区域之外的图像不会显示。而关键问题在于如何计算裁剪区域。
裁剪画布,在裁剪画布内的区域都是显示出来的,所以显示出来的区域才是裁剪区域。从图示中可以看出,有两个裁剪区域。
裁剪区域一:从左向右,逐渐变大。假设宽度是 clipWidth,高度是 CLIP_HEIGHT,那么裁剪区域一所对应的 Rect 对象如下:
裁剪区域二:从右向左,同样逐渐变大,它的宽度、高度都与裁剪区域一相同。但它是从右向左变化的,假设图片的宽度是 width,那么裁剪区域二所对应的 Rect 对象如下:
2. 示例代码
|
|